/*
	bob_manager.js
	Author: Jason Ganzhorn
*/

// Default size of tiles (on both sides).
var _TILE_SZ = 60;
// Minimum layer index
var _MC_MIN_HEIGHT = 0;
// Maximum layer index
var _MC_MAX_HEIGHT = 128;

// Default dimensions
var _DIM_X = 15;
var _DIM_Y = 15;

// layer manager
var layerManager = null;
var image_mgr = null;

// AJAX helper method
function ajaxFailure(ajax, exception) {
    alert("Error making Ajax request:" + 
          "\n\nServer status:\n" + ajax.status + " " + ajax.statusText + 
          "\n\nServer response text:\n" + ajax.responseText);
    if (exception) {
        throw exception;
    }
}


// Define object LayerManager to track collection of layers.
function LayerManager(startContent, sizeX, sizeY) {
	if(sizeX == undefined)
		sizeX = _DIM_X;
	if(sizeY == undefined)
		sizeY = _DIM_Y;

	// initialize array of layers
	this.layers = new Array();
	this.size = {x: sizeX, y: sizeY};
	this.tileSz = _TILE_SZ;
	
	// create the first layer
	this.createLayer(0);
	// enter initial content if it exists
	this.initLayers(startContent);
	// create layout tiles
	this.createTiles();
	// set current layer index
	this.layer_index = 0;
	// fill tiles with values from layer 0
	this.fillTiles();
	
	// initialize selection stuff
	this.selected_tiles = new Array();
	this.selecting = false;
	this.select_startTile = null;
	this.select_endTile = null;
	
	this.heightSelector = this.createHeightSelector();
}

LayerManager.prototype.cleanUp = function() {
	$("layout_tiles").innerHTML = "";
	$("layout").appendChild(this.heightSelector);
	this.heightSelector.hide();
};

LayerManager.prototype.createLayer = function(index) {
	if(index >= _MC_MIN_HEIGHT && index <= _MC_MAX_HEIGHT) {
		if(this.layers[index] != null && this.layers[index] != undefined)
			delete this.layers[index];
			
		this.layers[index] = new Array();

		for(var i = 0; i < this.size.x; i++) {
			this.layers[index][i] = new Array();
			for(var j = 0; j < this.size.y; j++) {
				this.layers[index][i][j] = 0;
			}
		}
	} else {
		throw "Index out of bounds.";
	}
};

LayerManager.prototype.createTiles = function() {
	// create the new tiles
	//this.tiles = new Array();
	for(var row = 0; row < this.size.y; row++) {
		for(var col = 0; col < this.size.x; col++) {
			var tile = new Tile(this, row, col, this.tileSz, 0);
			tile.addTileEventHandlers();
			//this.tiles.push(tile);
		}
	}
	
	$("layout_tiles").style.width = ((this.tileSz + 3) * this.size.x) + "px";
	$("layout_tiles").style.height = ((this.tileSz + 3) * this.size.y) + "px";
}

// Fill tiles using data in the layers array at the 'layer_index' index.
LayerManager.prototype.fillTiles = function() {
	for(var row = 0; row < this.size.y; row++) {
		for(var col = 0; col < this.size.x; col++) {
			var tileDiv = this.getTileByCoords(col, row);
			tileDiv.select("input")[0].value = this.layers[this.layer_index][col][row];
			image_mgr.setElementBackgroundByIndex(tileDiv.parentTile.typeDisplay, this.layers[this.layer_index][col][row]);
		}
	}
}

LayerManager.prototype.getTileByCoords = function(x, y) {
	return $(x + "_" + y);
};

LayerManager.prototype.initLayers = function(content) {
	if(content != null)
		this.layers = content.layers;
}

// Switch to a different layer.
LayerManager.prototype.switchToLayer = function(index) {
	// do no needless work...
	if(index == this.layer_index)
		return;
		
	// bound checking
	if(index < _MC_MIN_HEIGHT || index > _MC_MAX_HEIGHT)
		throw "Index out of bounds.";
	
	if(this.layers[index] == null || this.layers[index] == undefined)
		this.createLayer(index);
	
	this.layer_index = index;
	this.fillTiles();
};

// Serialize layers array for transmission.
LayerManager.prototype.sendLayers = function() {
	var tempForm = new Element("form");
	tempForm.action = "";
	tempForm.target = "_blank";
	tempForm.method = "POST";
	
	var data = {spawnUnderwater: "false", spawnIn: 3, layers: null};
	data.spawnUnderwater = $("underwater_spawn").checked ? "true" : "false";
	data.spawnIn = parseInt($F("spawn_mat"));
	data.layers = this.layers;
	
	var dataField = new Element("input");
	dataField.type = "hidden";
	dataField.name = "data";
	dataField.value = Object.toJSON(data);
	tempForm.appendChild(dataField);
	
	var taskField = new Element("input");
	taskField.type = "hidden";
	taskField.name = "task";
	taskField.value = "export";
	tempForm.appendChild(taskField);
	
	$("layout").appendChild(tempForm);
	tempForm.submit();
	tempForm.remove();
}

// Submit a .bob file to the web service and get received data.
// TODO: this is probably going to require converting index.html into a PHP file. OH well.
LayerManager.prototype.getLayers = function() {
	$("uploadbob").submit();
}

// start a new selection with given tile
LayerManager.prototype.startSelection = function(tile) {
	if(!this.selected_tiles.include(tile)) {
		this.clearSelection();
		this.selecting = true;
		this.select_startTile = tile;
		this.removeHeightSelector(tile);
	}
};

LayerManager.prototype.endSelection = function(tile) {
	if(this.selecting) {
		this.updateSelection(tile);
		this.selecting = false;
		this.addHeightSelector(tile);
	}
};

// update current selection to include 'tile' as an corner of the selection
LayerManager.prototype.updateSelection = function(tile) {
	if(this.selecting && this.select_endTile != tile) {
		this.select_endTile = tile;
		this.recalculateSelection();
	}
};

// update the content of the input textbox of each tile in the selection
LayerManager.prototype.updateSelectionContent = function(content) {
	this.selected_tiles.each(function(tile) {
		var tile_input = tile.tileDiv.select("input")[0];
		tile_input.value = content;
		image_mgr.setElementBackgroundByIndex(tile.typeDisplay, content);
		var lbound = Math.min(tile.manager.layer_index, tile.manager.layer_index + parseInt($F("layer_offset")));
		var ubound = Math.max(tile.manager.layer_index, tile.manager.layer_index + parseInt($F("layer_offset")));
		for(var i = lbound; i <= ubound; i++) {
			if(tile.manager.layers[i] == undefined)
				tile.manager.createLayer(i);
			tile.manager.layers[i][tile.col][tile.row] = content;
		}
	});
};

// clear current selection
LayerManager.prototype.clearSelection = function() {
	this.selected_tiles.each(function(tile) {
		tile.tileDiv.removeClassName("tile_selected");
	});
	
	delete this.selected_tiles;
	this.selected_tiles = new Array();
};

// recalculate selection (this is actually fairly efficient as long as the selection size is not too big)
LayerManager.prototype.recalculateSelection = function() {
	var startX, startY, endX, endY;
	startX = this.select_startTile.col;
	startY = this.select_startTile.row;
	endX = this.select_endTile.col;
	endY = this.select_endTile.row;
	
	// support selecting an area by moving the mouse cursor in any direction from the starting point
	sX = Math.min(startX, endX);
	eX = Math.max(startX, endX);
	sY = Math.min(startY, endY);
	eY = Math.max(startY, endY);
	
	// clear current selection
	this.clearSelection();
	
	// process all tiles within the new selection
	for(var row = sY; row <= eY; row++) {
		for(var col = sX; col <= eX; col++) {
			var tile = $(col + "_" + row);
			tile.addClassName("tile_selected");
			this.selected_tiles.push(tile.parentTile);
		}
	}
};

LayerManager.prototype.createHeightSelector = function() {
	var selector = $("height_selector");
	selector.setStyle({
		top: _TILE_SZ + "px",
		right: "0px"
	});
	$("layer_offset").value = 0;
	$("b_offset_down").observe("click", function() {
		var currOffset = parseInt($F("layer_offset"));
		var currLayerIndex = this.up(1).parentTile.manager.layer_index;
		if(currLayerIndex + currOffset > _MC_MIN_HEIGHT)
			$("layer_offset").value = parseInt(currOffset) - 1;
		var content = $F(this.up(1).select("input")[0]);
		layerManager.updateSelectionContent(content);
	});
	
	$("b_offset_up").observe("click", function() {
		var currOffset = parseInt($F("layer_offset"));
		var currLayerIndex = this.up(1).parentTile.manager.layer_index;
		if(currLayerIndex + currOffset < _MC_MAX_HEIGHT)
			$("layer_offset").value = parseInt(currOffset) + 1;
		var content = $F(this.up(1).select("input")[0]);
		layerManager.updateSelectionContent(content);
	});
	selector.show();
	selector.remove();
	
	return selector;
};

LayerManager.prototype.addHeightSelector = function(tile) {
	tile.tileDiv.appendChild(this.heightSelector);
	$("layer_offset").value = 0;
};

LayerManager.prototype.removeHeightSelector = function(tile) {
	if(this.heightSelector.up())
		this.heightSelector.remove();
};

function Tile(mgr, row, col, tileSz, startVal) {
	this.manager = mgr;
	this.row = row;
	this.col = col;
	
	// create HTML contents
	this.tileDiv = new Element("div");
	this.tileDiv.style.left = ((tileSz + 3) * col) + "px";
	this.tileDiv.style.top = ((tileSz + 3) * row) + "px";
	this.tileDiv.style.width = tileSz + "px";
	this.tileDiv.style.height = tileSz + "px";
	this.tileDiv.addClassName("tile");
	this.tileDiv.id = col + "_" + row;
	this.tileDiv.parentTile = this;
	$("layout_tiles").appendChild(this.tileDiv);
	
	var tileInput = new Element("input");
	tileInput.type = "text";
	if(startVal != undefined)
		tileInput.value = startVal;
	else
		tileInput.value = 0;
	this.tileDiv.appendChild(tileInput);
	
	this.typeDisplay = new Element("div");
	this.typeDisplay.addClassName("tiletype");
	this.parentTile = this;
	image_mgr.setElementBackgroundByIndex(this.typeDisplay, tileInput.value);
	this.tileDiv.appendChild(this.typeDisplay);
}

// register callbacks for HTML elements inside a tile
Tile.prototype.addTileEventHandlers = function() {
	this.tileDiv.observe("mouseover", this.mouseover);
	this.tileDiv.observe("mouseout", this.mouseout);
	this.tileDiv.observe("mousedown", this.mousedown);
	this.tileDiv.observe("mouseup", this.mouseup);
	
	// register callback for keyup event on input inside this tile
	var tileInput = this.tileDiv.select("input")[0];
	tileInput.observe("keyup", this.keyinput);
	tileInput.observe("click", function() {
		this.select();
	});
};

Tile.prototype.mouseover = function(event) {
	this.addClassName("tile_highlight");
	this.parentTile.manager.updateSelection(this.parentTile);
};

Tile.prototype.mouseout = function() {
	this.removeClassName("tile_highlight");
};

Tile.prototype.mousedown = function(event) {
	this.parentTile.manager.startSelection(this.parentTile);
	if(event.findElement().tagName.toLowerCase() != "input")
		event.stop();
};

Tile.prototype.mouseup = function() {
	this.parentTile.manager.endSelection(this.parentTile);
}

Tile.prototype.keyinput = function() {
	var mgr = this.up().parentTile.manager;
	mgr.updateSelectionContent(this.value);
}

function resetLayerManager() {
	layerManager.cleanUp();
	delete layerManager;
	layerManager = new LayerManager(null, $F("layer_size_x"), $F("layer_size_y"));
}

function parseContent(content) {
	if(content == "")
		return null;
	
	var lines = content.split("\n");
	var sizeX = _DIM_X;
	var sizeY = _DIM_Y;
	var i = lines.length-1;
	
	// get dimensions info
	for(; i > 0; i--) {
		var line = lines[i];
		if(line == "METBEGIN")
			break;
		
		if(line.substr(0, 4) == "dim:") {
			var dims = line.substr(4).split(",");
			if(dims.length < 2)
				continue;
				
			sizeX = parseInt(dims[0]);
			sizeY = parseInt(dims[1]);
		}
	}
	
	// construct layers array
	var layers = new Array();
	make_layer = function(index) {
		layers[index] = new Array(sizeX);
		for(var i = 0; i < sizeX; i++) {
			layers[index][i] = new Array(sizeY);
			for(var j = 0; j < sizeY; j++) {
				layers[index][i][j] = 0;
			}
		}
	};
	
	// indexing information
	var xc = 1;
	var yc = 0;
	var layer = 2;
	
	// get content
	for(i = 0; i < lines.length; i++) {
		var line = lines[i];
		// skip parsing meta-information
		if(line == "METBEGIN")
			break;
		
		var parts = line.split(":");
		if(parts.length < 2)
			continue;
			
		var coords = parts[0].split(",");
		if(coords.length < 3)
			continue;
			
		var content = parts[1].trim();
		if(layers[coords[layer]] == null || layers[coords[layer]] == undefined)
			make_layer(coords[layer]);
		layers[coords[layer]][coords[xc]][coords[yc]] = content;
	}
	
	var spawnMat = lines[i+1];
	var spawnUnderwater = lines[i+2];
	
	return {size: {x: sizeX, y: sizeY}, "layers": layers, "spawnUnderwater": spawnUnderwater, "spawnMat": spawnMat};
}

document.observe("dom:loaded", function() {
	// load terrain image
	image_mgr = new ImageManager("images/terrain.png");
	//image_mgr.setElementBackgroundByIndex($("0_0"), 17.2);

	var fileinfo = parseContent($("importfile").innerHTML);
	if(fileinfo != null) {
		$("layer_size_x").value = fileinfo.size.x;
		$("layer_size_y").value = fileinfo.size.y;
		$("underwater_spawn").checked = (fileinfo.spawnUnderwater == "true") ? true : false;
		var selectedOption;
		for(var i = 0; i < $("spawn_mat").options.length; i++) {
			var temp = $("spawn_mat").options.item(i);
			if(temp.value == fileinfo.spawnMat) {
				temp.selected = true;
				break;
			}
		}
	}
	layerManager = new LayerManager(fileinfo, $F("layer_size_x"), $F("layer_size_y"));
		
	$("importfile").innerHTML = "";
	
	$("b_clear_layer").observe("click", function() {
		layerManager.createLayer(layerManager.layer_index);
		layerManager.fillTiles();
	});
	
	$("b_clear_alllayers").observe("click", resetLayerManager);
	$("b_change_sz").observe("click", resetLayerManager);
	
	$("layer_up").observe("click", function() {
		if($F("layer_index") < _MC_MAX_HEIGHT) {
			var newLayerIndex = parseInt($F("layer_index")) + 1;
			$("layer_index").value = newLayerIndex;
			layerManager.switchToLayer(newLayerIndex);
		}
	});
	$("layer_down").observe("click", function() {
		if($F("layer_index") > _MC_MIN_HEIGHT) {
			var newLayerIndex = parseInt($F("layer_index")) - 1;
			$("layer_index").value = newLayerIndex;
			layerManager.switchToLayer(newLayerIndex);
		}
	});
	
	$("b_layer_go").observe("click", function() {
		var currIndex = parseInt($F("layer_index"));
		if(currIndex >= _MC_MIN_HEIGHT && currIndex <= _MC_MAX_HEIGHT) {
			layerManager.switchToLayer(currIndex);
		}
	});
	
	$("b_file_export").observe("click", function() {
		layerManager.sendLayers();
	});
	
	$("b_file_import").observe("click", function() {
		layerManager.getLayers();
	});
});
